""" Survey

Some code taken from ATTopic in ATContentTypes.
Licensed under the GPL.

The rest:
(C) 2004 by Dan Keshet (also under the GPL)

"""
from Products.Archetypes.public import OrderedBaseFolder, registerType, Schema
from Products.Archetypes.public import BooleanField, BooleanWidget, StringField, StringWidget
from Products.CMFCore.utils import getToolByName

from Products.ATContentTypes.content.document import ATDocument
from Products.ATContentTypes.content.document import finalizeATCTSchema
from Products.CMFPlone.interfaces.NonStructuralFolder import INonStructuralFolder

from Products.ArchSurvey.Permissions import *
from AccessControl import ClassSecurityInfo
from BTrees.OOBTree import OOBTree
from Persistence import Persistent

from Products.ArchSurvey.questions import QuestionRegistry

SurveySchema = ATDocument.schema.copy() + Schema((
    BooleanField('multiple',
                 required=1,
                 default=0,
                 widget=BooleanWidget(
                    label="Allow users to take multiple times?",
                    description=("If checked, users will be allowed to take the "
                                 "survey many times, with the dates recorded."),
                    visible={'view' : 'invisible'},
                    i18n_domain="archsurvey"),
                 ),
    StringField('destination',
                widget = StringWidget(
                        description = "The destination survey takers go after they complete the survey (leave blank for default)",
                )),
),)

SurveySchema['text'].required =0 
finalizeATCTSchema(SurveySchema)

class SurveyData(Persistent):
    """Survey data objects encapsulate information about a survey."""
    #browser_id = None #string (From the latest SESSION object)
    #started = None    #float representing DateTime
    finished = None   #float representing Datetime
    uid = None        #int
    last_page = None  #string (page_name)
    
#    def __init__(self, uid, browser_id):
    def __init__(self, uid ):
        """A new survey-taking is starting"""
        self.uid = uid
        #self.browser_id = browser_id        
        #import time
        #self.started = time.time()

class Survey(OrderedBaseFolder, ATDocument):
    """Surveys can be edited, taken, and analyzed within the Plone interface.  
       They offer a variety of question types.
    """
    schema = SurveySchema
    content_icon   = 'survey_icon.png'
    meta_type      = 'Survey'
    archetype_name = 'Survey'
    use_folder_tabs = 0
    filter_content_types  = 1
    allowed_content_types = ['LongAnswerQuestion',  
                             'ShortAnswerQuestion', 
                             'LabelQuestion',       
                             'PickOneQuestion',     
                             'PickManyQuestion',    
                             'YesNoQuestion',       
                             'NumberQuestion',      
                             'DateAndTimeQuestion', 
                             'ListQuestion',        
                             'RankingsQuestion' ]

    default_view = 'survey_results'
    immediate_view = 'survey_questions'
    _at_rename_after_creation = True

    __implements__ = ATDocument.__implements__  + (INonStructuralFolder,)

    actions = (
        {
        'id'          : 'view',
        'name'          : 'View',
        'action'      : 'string:${folder_url}',
        'permissions' : (VIEW_CONTENT_PERMISSION,),
        'visible'     : 0
        },
        {
        'id'          : 'edit',
        'name'        : 'Edit',
        'action'      : 'string:${folder_url}/edit',
        'permissions' : (EDIT_CONTENT_PERMISSION,)
        },
        {
        'id'          : 'questions',
        'name'        : 'Questions',
        'action'      : 'string:${folder_url}/questions',
        'permissions' : (EDIT_CONTENT_PERMISSION,)
        },
        {
        'id'          : 'results',
        'name'        : 'Results',
        'action'      : 'string:${folder_url}/results',
        'permissions' : (VIEW_RESULTS,)
        },
        {
        'id'          : 'take',
        'name'        : 'Take Survey',
        'action'      : 'string:${folder_url}/take',
        'permissions' : (TAKE_SURVEY,)
        },
        {
        'id'          : 'sharing',
        'name'        : 'Sharing',
        'action'      : 'string:${folder_url}/sharing',
        'permissions' : (EDIT_CONTENT_PERMISSION,)
        }
    )
    aliases = {
         '(Default)'  : 'survey_view',
         'view'       : 'survey_view',
         'index.html' : 'survey_view',
         'edit'       : 'atct_edit',
         'sharing'    : 'folder_localrole_form',
         'gethtml'    : '',
         'mkdir'      : '',
         'questions'  : 'survey_questions',
         'results'    : '(selected layout)',
         'take'       : 'survey_take',
         }

    security = ClassSecurityInfo()
    
    def __init__(self, id, **kwargs):
        self._users = OOBTree()
        self._pages = ['1']
        self.uid = 0
        OrderedBaseFolder.__init__(self,id, **kwargs)
    
    def canSetDefaultPage(self):
        """Disallow setting a "default page" to override the view"""
        return False

    def manage_afterClone(self, item):
        """Get rid of the list of users who have taken the survey
        and start afresh"""
        self._users = OOBTree()
        OrderedBaseFolder.manage_afterClone(self, item)
        
    security.declareProtected(TAKE_SURVEY, 'getUniqueId')
    def getUniqueId(self, user, request=None, make_new=True):
        """Returns a different id for each username (or browser id in case of anon.), 
        so that we can decide whether to keep track of who answered what or not"""

        if type(user) == type(''):
            userid = user
        else:
            try:
                userid = user.getId()                
            except AttributeError:
                raise AttributeError("Not a user %s" % user)
        #This looks like a problem.  How do we get here?  Problems w/anons!
        if userid is None:
            ##Get the browser key.
            try:
                browser_id = request.SESSION.getBrowserIdManager().getBrowserId()
            except AttributeError:
                browser_id = ''
            userid = 'Anonymous User (%s) ' % browser_id
        # Get the user's existing session, if any.
        l = []
        if self._users.has_key(userid):
            l = self._users[userid]
            if type(l) == type(0): #old-style uid
                return l
            elif l[-1].finished is None or not self.getMultiple(): 
                return l[-1].uid
        # We're still here if a) the user hasn't taken the survey, 
        # or b) the user is allowed to take it multiple times and they 
        # have already finished the last time they took it.
        if make_new: 
            self._users[userid] = l + [ SurveyData(self.uid) ]
            self.uid = self.uid + 1
            return self.uid - 1
        
    security.declareProtected(VIEW_RESULTS, 'num_finished_surveys')
    def num_finished_surveys( self, user ):
        """returns how many finished surveys a user has done"""
        if self._users.has_key( user ):
            return len( [x for x in self._users[ user ] if x.finished is not None ])
        return 0
        
    security.declareProtected(TAKE_SURVEY, 'num_finished_own_surveys')
    def num_finished_own_surveys( self ):
        """returns how many finished surveys the current user has done"""
        pm = getToolByName( self, 'portal_membership')
        username = pm.getAuthenticatedMember().getId()
        return self.num_finished_surveys( username )
        
    security.declareProtected(VIEW_RESULTS, 'listRespondents')
    def listRespondents(self, only_finished=True):
        """List of people who have taken the survey"""
        all_users = list(self._users.keys())         
        if only_finished:
            return filter(lambda x:x.finished is not None,all_users)
        return all_users
    
    security.declareProtected(VIEW_RESULTS, 'getResults')
    def getResults(self):
        """Gets a list of lists of results"""
        results = []
        pages = self.getPages()
        fields = self.listQuestions(skiplabels=1)        
        for user, surveys in self._users.items():
            for s in surveys:
                #only get answers to questions the user answered
                #answers = [(field.accessor(s.uid)) for field in
                #           self.listQuestions(pages=pages[:last_page])] 
                #answers = answers + ['' for x in fields][:len(answers)]
                answers = [(field.accessor(s.uid)) for field in fields] 
                results.append( [user, s.last_page, s.finished] + answers)
        return results
            
    security.declareProtected(EDIT_CONTENT_PERMISSION, 'clearResults')
    def clearResults(self):
        """Removes the current results (e.g. testing results)"""
        self._users = OOBTree()
        for q in self.listQuestions(skiplabels=1):
            q.clearResults()
    
    security.declareProtected(EDIT_CONTENT_PERMISSION, 'addQuestion')
    def addQuestion(self, field_type, page=None):
        """Add a new question.
        """
        if page is None:
            page = self.getPages()[0]
        newid = self.generateUniqueId(field_type)
        ft    = QuestionRegistry.get(field_type,None)
        if ft is None:
            raise ValueError, "%s is not a valid question type" % field_type
        question = ft(newid,schemata=page)
        self._setObject( newid, question)
        return newid
        
    def listQuestionTypes(self):
        """List available question types"""
        qtypes = QuestionRegistry.listTypes()
        selftypes = [x.Metatype() for x in self.allowedContentTypes() ]
        return [x for x in qtypes if x in selftypes] 
    
    def listQuestionTypeValues(self):
        """List available question types with meta_types."""
        results = QuestionRegistry.listMetaAndShort()
        selftypes = [x.Metatype() for x in self.allowedContentTypes() ]
        return [x for x in results if x[0] in selftypes]
    
    def listQuestions(self, pages=None, skiplabels=0):
        """List current questions,optionally restricting pages or types"""
        types=None
        if skiplabels:
            types = [x for x in self.listQuestionTypes()]
        values = self.contentValues(spec=types)
        if pages is None:
            return values
        if type(pages) is not type([]):
            pages = list(pages)
        values = [q for q in values if q.getLocalSchemata() in pages]
        if skiplabels:
            return [x for x in values if not x.isLabel()]
        return values
        
    security.declareProtected(EDIT_CONTENT_PERMISSION, 'addPage')
    def addPage(self,cur_page):
        l = self.getPages()
        i = 1
        while i:
            if str(i) not in l:
                break
            i += 1
        i = str(i)
        try:
            idx = l.index(cur_page)
            l.insert(l.index(cur_page)+1,i)
        except ValueError:
            l.append(i)
        self._pages = l
        return i
        
    def delPage(self,page):
        """Remove all questions on a particular page and the page itself"""
        self.manage_delObjects([x.id for x in self.listQuestions(pages=page)])
        if len(self._pages) > 1:
            try:
                idx = self._pages.index(page) - 1
                self._pages.remove(page)
                self._pages = self._pages #or use the _p_changed magic.
                return self._pages[idx]
            except ValueError:
                return None
        
    def getPages(self):
        """Return all pages"""
        return self._pages
    
    def markTaken(self, member, uid, page=None):
        """Mark a page as having been taken"""
        if page is None:
            page = self._pages[-1]
        surveys = filter(lambda x:x.uid==uid,self._users[member])
        if len(surveys) != 1:
            raise KeyError("Corrupt data!. Length: %d, uid: %d" % (len(surveys),uid))
        survey = surveys[0]
        survey.last_page = page
        #if this is the last page, mark the survey as having been finished too
        if page == self._pages[-1]:
            import time
            survey.finished = time.time()        
    

registerType(Survey)
